package controller;

import guimessage.RemoteDisconnect;
import itdb1.commun.*;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;

import network.LocalNetworkSettings;
import network.UDPChatNIServer;
import network.UDPTransmitter;

public class UDPServerController extends GenericController {
	
	private static UDPServerController instance = null;
	
	public ArrayBlockingQueue<NetworkSignal> messages;
	
	private GUIController guicontroller;
	
	private UDPChatNIServer udpserver;
	private boolean isrunning;
	
	private UDPServerController()
	{
		super();
		this.guicontroller = GUIController.getInstance();
		messages = new ArrayBlockingQueue<NetworkSignal>(10);
	}

	public static UDPServerController getInstance()
	{
		if(instance == null)
			instance = new UDPServerController();
		
		return instance;
	}
	
	@Override
	public void run() {
		isrunning = true;
		
		NetworkSignal m = null;
		
		guicontroller.writeDebugScreen("UDPServerController > launched\n");
		
		// Launching of UDP server
		udpserver = new UDPChatNIServer(LocalNetworkSettings.LOCAL_UDP_SERVER_PORT);
		( new Thread(udpserver)).start();
		
		do
		{
			try
			{
				m = messages.take();
			}
			catch(InterruptedException e)
			{
				System.out.println("Stop de take()");
			}
			
			if( m.getClass() == Hello.class )
			{
				processHello((Hello) m);
			}
			else if ( m.getClass() == HelloAck.class )
			{
				processHelloAck((HelloAck) m);
			}
			else if ( m.getClass() == Bye.class )
			{
				processBye((Bye) m);
			}
			else if ( m.getClass() == Message.class )
			{
				processMessage((Message) m);
			}
			else if (m.getClass() == HeartBeats.class)
			{
				processHeartBeats((HeartBeats) m);
			}
			else
			{
				guicontroller.writeDebugScreen("UDPServerController > bad message received !\n");
			}
		
		} while(isrunning);
		
		guicontroller.writeDebugScreen("UDPServerController > shut down\n");
	}

	public void kill()
	{
		isrunning = false;
		udpserver.kill();
		messages.clear();
		messages.add( new Bye(null) );
	}
	
	private void processHello(Hello m)
	{
		guicontroller.writeDebugScreen("UDPServerController > Hello received from " + m.getUsername() + "@" + m.getFromIp().getHostName() + "\n");
		
		if(this.contacts.present(m.getFromIp()))
		{
			this.contacts.update(m.getFromIp(), new Date());
		}
		else
			this.contacts.add(new Contact( m.getUsername(), m.getFromIp(), new Date() ));
		
		// envoie du message
		new UDPTransmitter( new HelloAck(contacts.getLocalusername(), m.getFromIp()), LocalNetworkSettings.REMOTE_UDP_SERVER_PORT );
	}
	
	private void processHelloAck(HelloAck m)
	{
		guicontroller.writeDebugScreen("UDPServerController > AckHello received !\n");
		
		if(this.contacts.present(m.getFromIp()))
		{
			this.contacts.update(m.getFromIp(), new Date());
		}
		else
			this.contacts.add(new Contact( m.getUsername(), m.getFromIp(), new Date() ));
	}
	
	private void processBye(Bye m)
	{
		if(m.getFromIp() != null)
		{
			if( this.contacts.present(m.getFromIp()) )
			{
				guicontroller.writeDebugScreen("UDPServerController > Bye received from " + m.getFromIp().getHostAddress() + "!\n");
			
				RemoteDisconnect m_to_gui = new RemoteDisconnect(m.getFromIp());
				guicontroller.messages.add(m_to_gui);
			}
		}
	}
	
	private void processHeartBeats(HeartBeats m)
	{
		guicontroller.writeDebugScreen("UDPServerController > HeartBeat received from "
				+ m.getFromIp().getHostAddress() + "\n");

		if( this.contacts.present( m.getFromIp() ) )
		{
			this.contacts.update(m.getFromIp(), new Date());
		}
		else
		{
			try
			{
				new UDPTransmitter( new Hello(this.contacts.getLocalusername(), InetAddress.getByName("255.255.255.255")), LocalNetworkSettings.REMOTE_UDP_SERVER_PORT);
			}
			catch(UnknownHostException e)
			{// On aura jamais cette erreur de toute façon
			}
		}
	}
	
	private void processMessage(Message m)
	{
		guicontroller.writeDebugScreen("UDPServerController > Message received from "
										+ m.getFromIp().getHostAddress() + "\n\t\t" + m.getMessage());
		
		if(this.contacts.present(m.getFromIp()))
		{
			this.contacts.update(m.getFromIp(), new Date());
			guicontroller.writeDiscussionTab(m);
		}
		else
		{
			try
			{
				new UDPTransmitter( new Hello(this.contacts.getLocalusername(), InetAddress.getByName("255.255.255.255")), LocalNetworkSettings.REMOTE_UDP_SERVER_PORT);
			}
			catch(UnknownHostException e)
			{// On aura jamais cette erreur de toute façon
			}
		}
	}
}
